using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Data.Common;
using System.Data;
using System.Text;
using System;
using System.Globalization;
using System.Web.Configuration;
using ResourceBlender.DAL;
using ResourceBlender.Core;
using ResourceBlender.NET;

/// <summary>
/// Shows duplicate resources in a grid and lets a user re-assign elements to a new resource and delete resources.
/// </summary>
public partial class DuplicateResources : LocalPage {
    /// <summary>
    /// Index of the controls in each dynamic cell
    /// </summary>
    private static class SelectedRowControlIndexes { // todo: why is this a class? - make it an enum
        public const int txtTranslation = 1,
                         btnFindSimilar = 3,
                         btnGoogle = 4;
    }
    private const int editButtonColumNo = 3;

    //private IDictionary<int, string> resourceIds = new Dictionary<int, string>();
    private IEnumerable<KeyValuePair<int, string>> resourceIds = new Dictionary<int, string>();
    private StringBuilder jQueryBuffer;

    /// <summary>
    /// Fills the raddio boxes for each language in the database
    /// </summary>
    /// <param name="sender">The source of the event.</param>
    /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
    protected void Page_Load(object sender, EventArgs e) {
        if(!Page.IsPostBack) {
            IEnumerable<KeyValuePair<string, CultureInfo>> langs = ResourceBlenderDAL.GetUniqueLanguages();
            foreach(var lang in langs) {
                radLanguage.Items.Add(new ListItem(lang.Value.EnglishName, lang.Key));
            }
            if(langs.Any()) {
                radLanguage.SelectedIndex = 0;
            }
            SelectLanguage(null, EventArgs.Empty);
        } else {
            if(mltMain.ActiveViewIndex == 1) {
                InitializeDetailsDataSource();
            }
        }
        lblMergeError.Visible = false;
        Page.ClientScript.RegisterClientScriptInclude("json", ResolveUrl("~/js/jquery.json-1.3.min.js"));
        Page.ClientScript.RegisterClientScriptInclude("resourcejs", ResolveUrl("~/js/resources.js"));
        ScriptManager.RegisterStartupScript(this, typeof(Page), "nojs", "$(function(){ $('#" + btnRefresh.ClientID + "').remove(); });", true);
        Master.RenderJSArrayWithCliendIds(fullsizeholder);
        jQueryBuffer = new StringBuilder();
    }

    /// <summary>
    /// Outputs the jQuery buffer to JavaScript
    /// Page_PreRender > Controls Pre_Render > Page_SaveStateComplete
    /// </summary>
    /// <param name="sender">The source of the event.</param>
    /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
    protected void Page_SaveStateComplete(object sender, EventArgs e) {
        jQueryBuffer.Insert(0, "var prm = Sys.WebForms.PageRequestManager.getInstance(); prm.add_endRequest(function() { \n");
        jQueryBuffer.Append("});");
        ScriptManager.RegisterStartupScript(this, typeof(Page), "jquery", jQueryBuffer.ToString(), true);
    }

    /// <summary>
    /// Initializes the datasource and rebinds the grid.
    /// </summary>
    private void BindGrid() {
        grdFindDuplicates.DataBind();
        if(grdFindDuplicates.Rows.Count == 0) {
            grdFindDuplicates.GridLines = GridLines.None;
        } else {
            grdFindDuplicates.GridLines = GridLines.Both;
        }
    }

    /// <summary>
    /// Filters duplicate resources according to language
    /// </summary>
    /// <param name="sender">The sender.</param>
    /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
    protected void SelectLanguage(object sender, EventArgs e) {
        if(radLanguage.SelectedItem != null) {
            BindGrid();
        }
    }

    /// <summary>
    /// Shows the number of items on all pages of the GridView
    /// </summary>
    /// <param name="sender">The source of the event.</param>
    /// <param name="e">The <see cref="System.Web.UI.WebControls.ObjectDataSourceStatusEventArgs"/> instance containing the event data.</param>
    protected void dsMain_Selected(object sender, ObjectDataSourceStatusEventArgs e) {
        lblNumDuplicates.Text = I18nUtils.GetString("UIStrings", "NumDuplicatesForLang", (e.ReturnValue as DataTable).Rows.Count.ToString(), radLanguage.SelectedItem.Text);
    }

    /// <summary>
    /// Handles RowCommands from the details grid.
    /// </summary>
    /// <param name="sender">The source of the event.</param>
    /// <param name="e">The <see cref="System.Web.UI.WebControls.GridViewCommandEventArgs"/> instance containing the event data.</param>
    protected void grdFindDuplicates_RowCommand(object sender, GridViewCommandEventArgs e) {
        BindGrid();
        switch(e.CommandName) {
            case "viewdetails":
                int resourceId = int.Parse(e.CommandArgument.ToString());
                //todo: trap exception - minor
                string selectedLanguage = radLanguage.SelectedValue;
                string duplicateResource = ResourceBlenderDAL.GetResource(resourceId, new string[] {selectedLanguage}).Translations[selectedLanguage].Translation;
                Session["duplicateResource"] = duplicateResource;
                mltMain.ActiveViewIndex = 1;
                GridViewRow row = ((e.CommandSource as WebControl).NamingContainer as GridViewRow);
                InitializeDetailsDataSource();
                grdFindDuplicates.SelectedIndex = row.RowIndex;
                grdDuplicateDetail.DataBind();
                cboPreferredResource.DataBind();
                break;
        }
    }

    /// <summary>
    /// Changes the page shown in the resource grid.
    /// </summary>
    /// <param name="sender">The source of the event.</param>
    /// <param name="e">The <see cref="System.Web.UI.WebControls.GridViewPageEventArgs"/> instance containing the event data.</param>
    protected void grdFindDuplicates_PageIndexChanging(object sender, GridViewPageEventArgs e) {
        grdFindDuplicates.PageIndex = e.NewPageIndex;
        BindGrid();
    }

    /// <summary>
    /// Handles sorting for the resource grid.
    /// </summary>
    /// <param name="sender">The source of the event.</param>
    /// <param name="e">The <see cref="System.Web.UI.WebControls.GridViewSortEventArgs"/> instance containing the event data.</param>
    protected void grdFindDuplicates_Sorting(object sender, GridViewSortEventArgs e) {
        string oldExpression = grdFindDuplicates.SortExpression,
               newExpression = e.SortExpression;
        if(oldExpression.IndexOf(newExpression) < 0) {
            if(oldExpression.Length > 0) {
                e.SortExpression = newExpression + "," + oldExpression;
            } else {
                e.SortExpression = newExpression;
            }
        } else {
            e.SortExpression = oldExpression;
        }
        BindGrid();
    }

    #region View Duplicates
    /// <summary>
    /// Cancels any editing in progress in the grid and hides all edit controls.
    /// </summary>
    private void CancelEditing() {
        if(grdDuplicateDetail.SelectedIndex >= 0 && grdDuplicateDetail.SelectedIndex < grdDuplicateDetail.Rows.Count) {
            grdDuplicateDetail.SelectedIndex = -1;
            grdDuplicateDetail.EditIndex = -1;
        }
    }

    /// <summary>
    /// Initializes the ResourceID and main DataSources.
    /// </summary>
    private void InitializeDetailsDataSource() {
        if(resourceIds.Count() < 5) {
            Dictionary<string, int> columnIndex = new Dictionary<string, int>();
            string selectedLanguage = radLanguage.SelectedValue;
            IEnumerable<KeyValuePair<string, CultureInfo>> langs = ASPNETBLL.GetUniqueLanguages();

            // show the selected language as the first column
            var languagesWithoutSelected = langs.Where(l => l.Key != selectedLanguage).Select(l => l.Key);
            List<string> tempLanguages = new List<string>();
            tempLanguages.Add(selectedLanguage);
            tempLanguages.AddRange(languagesWithoutSelected);

            string[] languages = tempLanguages.ToArray();

            for(int i = 0; i < languages.Length; i++) {
                columnIndex[languages[i]] = i + editButtonColumNo + 1;
            }
            grdDuplicateDetail.ColumnsGenerator = new LanguageColumnGenerator(languages);
            Session["colIndexes"] = columnIndex;
            Session["languages"] = languages;
            //!!
            //dsDuplicateDetails.SelectParameters["languages"].DefaultValue = null;
        }
    }

    /// <summary>
    /// Databinds the ResourceID combobox and assigns JavaScript listeners.
    /// </summary>
    /// <param name="sender">The source of the event.</param>
    /// <param name="e">The <see cref="System.Web.UI.WebControls.GridViewRowEventArgs"/> instance containing the event data.</param>
    protected void grdDuplicateDetail_RowDataBound(object sender, GridViewRowEventArgs e) {
        DataRowView view = e.Row.DataItem as DataRowView;
        if(e.Row.RowType == DataControlRowType.DataRow) {
            if(((e.Row.RowState & DataControlRowState.Edit) == DataControlRowState.Edit)) {
                GridViewRow row = e.Row;
                row.Cells[2].CssClass = "ssCell"; // remove the comment indicators
                row.Cells[3].CssClass = "ssCell";

                int textBoxControlIndex = row.Cells[editButtonColumNo + 1].Controls.Count - 1;// the comments textbox is the last control in the view
                int resourceid = int.Parse(DataBinder.Eval(e.Row.DataItem, "tid").ToString());

                // load translator comments
                IEnumerable<string> displayedLanguages = ASPNETBLL.GetUniqueLanguages().Select(pair => pair.Key);
                Dictionary<string, int> columnIndex = Session["colIndexes"] as Dictionary<string, int>;
                Dictionary<string, string> translatorComments = ResourceBlenderDAL.GetTranslatorComments(resourceid, displayedLanguages);
                foreach(var comment in translatorComments) {
                    (row.Cells[columnIndex[comment.Key]].Controls[textBoxControlIndex] as TextBox).Text = comment.Value;
                }

                DropDownList cboResourceId = row.FindControl("cboResourceID") as DropDownList;
                cboResourceId.SelectedValue = view["tid"].ToString();

                // find the JS callback which is assigned to the 'Change Resource' button
                Button btnChangeResource = row.FindControl("btnChangeResource") as Button;
                string jsChangeResource = ClientScript.GetPostBackClientHyperlink(btnChangeResource, "");
                cboResourceId.Attributes["onchange"] = jsChangeResource; // assign the callback to the dropdown list so a button press is simulated when the index changes
            } else {
                string resourceComment = (DataBinder.Eval(e.Row.DataItem, "resourcecomment") ?? string.Empty).ToString();
                if(resourceComment.Length > 0) {
                    e.Row.Cells[3].ToolTip = resourceComment;
                    e.Row.Cells[3].CssClass = "ssCell commentCell";
                }
            }
            //System.Diagnostics.Debug.WriteLine("rowdatabound");
        }
    }


    /// <summary>
    /// Changes the page shown in the duplicate details grid.
    /// </summary>
    /// <param name="sender">The source of the event.</param>
    /// <param name="e">The <see cref="System.Web.UI.WebControls.GridViewPageEventArgs"/> instance containing the event data.</param>
    protected void grdDuplicateDetail_PageIndexChanging(object sender, GridViewPageEventArgs e) {
        grdDuplicateDetail.PageIndex = e.NewPageIndex;
        grdDuplicateDetail.DataBind();
    }

    /// <summary>
    /// Sets up JQuery event listeners for the 'Find Resources' button
    /// </summary>
    /// <param name="sender">The source of the event.</param>
    /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
    protected void grdDuplicateDetail_PreRender(object sender, EventArgs e) {
        if(grdDuplicateDetail.SelectedIndex >= 0 && grdDuplicateDetail.Rows.Count > grdDuplicateDetail.SelectedIndex) {
            GridViewRow row = grdDuplicateDetail.Rows[grdDuplicateDetail.SelectedIndex];
            Label lblResourceIdDesc = row.FindControl("lblResourceIdDesc") as Label;
            if(lblResourceIdDesc.Visible) { // if the row is invisible, the elements using this resource have been re-assigned and there is no need to register the AJAX search
                // The control indexes of the textbox and image buttons to which to attach event listeners
                TableCell cell;
                TextBox txt;
                HyperLink btnFindSimilar;
                string rowId;
                for(int i = editButtonColumNo + 1; i < row.Cells.Count; i++) {
                    rowId = row.Cells[i].ClientID;
                    cell = row.Cells[i];
                    txt = cell.Controls[SelectedRowControlIndexes.txtTranslation] as TextBox;
                    btnFindSimilar = cell.Controls[SelectedRowControlIndexes.btnFindSimilar] as HyperLink;
                    ASPNETUtils.AssignFindSimilarListener(jQueryBuffer, btnFindSimilar.ClientID, txt.ClientID, rowId, btnFindSimilar.NavigateUrl.Substring(1), false);
                }
            }
        }
        //System.Diagnostics.Debug.WriteLine("prerender");
    }

    /// <summary>
    /// Handles RowCommands from the resource grid.
    /// </summary>
    /// <param name="sender">The source of the event.</param>
    /// <param name="e">The <see cref="System.Web.UI.WebControls.GridViewCommandEventArgs"/> instance containing the event data.</param>
    protected void grdDuplicateDetail_RowCommand(object sender, GridViewCommandEventArgs e) {
        InitializeDetailsDataSource();
        grdDuplicateDetail.DataBind();
        GridViewRow row;  // The row which has clicked was the undatabound one, so use it's index to lookup the new, databound row
        string cmd = e.CommandName.ToLowerInvariant();
        int selectedResourceID = -1;
        switch(cmd) {
            case "editresource": // 'Edit' button clicked
                int rowIndex = ((e.CommandSource as WebControl).NamingContainer as GridViewRow).RowIndex;
                if(rowIndex < grdDuplicateDetail.Rows.Count) {
                    row = grdDuplicateDetail.Rows[rowIndex];
                    grdDuplicateDetail.SelectedIndex = row.RowIndex;
                    grdDuplicateDetail.EditIndex = row.RowIndex;
                }
                break;
            case "canceledit":
                row = grdDuplicateDetail.Rows[((e.CommandSource as WebControl).NamingContainer as GridViewRow).RowIndex];
                CancelEditing();
                break;
            case "changeresource": // index of dropdownlist changed
                row = (e.CommandSource as WebControl).NamingContainer as GridViewRow;
                selectedResourceID = int.Parse((row.FindControl("cboResourceID") as DropDownList).SelectedValue);
                //ChangeDisplayedResource(row, selectedResourceID);
                break;
            case "saveresource": // 'Save' button clicked
                grdDuplicateDetail.DataBind();
                row = (e.CommandSource as WebControl).NamingContainer as GridViewRow;
                UpdateResource(row, int.Parse(e.CommandArgument.ToString()));
                break;
            case "deleteresource":
                row = grdDuplicateDetail.Rows[((e.CommandSource as WebControl).NamingContainer as GridViewRow).RowIndex];
                CancelEditing();
                grdDuplicateDetail.PreRender += (s, pree) => {
                    GridViewRow newRrow = grdDuplicateDetail.Rows[row.RowIndex];
                    (newRrow.FindControl("mltActions") as MultiView).ActiveViewIndex = 1;
                };
                ///ConfirmDelete(row, selectedResourceID);
                break;
            case "confirmdelete":
                row = grdDuplicateDetail.Rows[((e.CommandSource as WebControl).NamingContainer as GridViewRow).RowIndex];
                //selectedResourceID = int.Parse((row.Cells[editButtonColumNo].FindControl("cboResourceID") as DropDownList).SelectedValue);
                selectedResourceID = int.Parse((row.Cells[1].FindControl("chkSelect") as CheckBox).Text);
                ResourceBlenderDAL.DeleteResource(int.Parse(e.CommandArgument.ToString()));//selectedResourceID);
                grdDuplicateDetail.DataBind();
                break;
            case "showelements":
                row = grdDuplicateDetail.Rows[((e.CommandSource as WebControl).NamingContainer as GridViewRow).RowIndex];
                ShowElements(row, e.CommandArgument.ToString());
                break;
        }
    }

    /// <summary>
    /// When the index changes, this event is fired before the RowCommand handler kicks in and takes the hidden button click.
    /// After RowCommand, the rows are re-databound. In this handler, another handler is hooked up for pre-render to change the values to the displayed resource.
    /// Summary: SelectedIndexChanged -> RowCommand -> RowDataBound -> PreRender
    /// </summary>
    /// <param name="sender">The source of the event.</param>
    /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
    protected void cboResourceID_SelectedIndexChanged(object sender, EventArgs e) {
        DropDownList cbo = sender as DropDownList;
        GridViewRow row = cbo.NamingContainer as GridViewRow;
        int selectedResource = int.Parse(cbo.SelectedValue); // Index that was just selected.
        int rowIndex = row.RowIndex; // the row index, the index doesn't change after databinding

        RBResource res = ResourceBlenderDAL.GetResource(selectedResource, ASPNETBLL.GetUniqueLanguages().Select(pair =>pair.Key));
        PreserveResource(rowIndex, res);
    }

    /// <summary>
    /// Preserves a resource through DataBinding by adding it after PreRender.
    /// </summary>
    /// <param name="rowIndex">Index of the row in the GridView.</param>
    /// <param name="res">The resource.</param>
    private void PreserveResource(int rowIndex, RBResource resource) {
        grdDuplicateDetail.PreRender += (s, pree) => {
            GridViewRow row = grdDuplicateDetail.Rows[rowIndex];// the new row, after databinding
            DropDownList newCbo = row.FindControl("cboResourceID") as DropDownList;
            newCbo.SelectedValue = resource.ID.ToString();
            if(row.Cells.Count <= 4) // row unbound for some reasom
                return;

            int txtCommentControlIndex = row.Cells[editButtonColumNo + 1].Controls.Count - 1;// the comments textbox is the last control in the view
            if(resource.ID < 0) {
                View viwEdit;
                for(int i = editButtonColumNo + 1; i < row.Cells.Count; i++) {
                    viwEdit = (row.Cells[i].Controls[0] as MultiView).Views[1];
                    (viwEdit.Controls[SelectedRowControlIndexes.txtTranslation] as TextBox).Text = "";
                    (viwEdit.Controls[txtCommentControlIndex] as TextBox).Text = "";
                }
            } else {
                Dictionary<string, int> columnIndex = Session["colIndexes"] as Dictionary<string, int>;
                foreach(var translation in resource.Translations) {
                    TableCell cell = row.Cells[columnIndex[translation.Key]]; // the cell with the edit controls
                    (cell.Controls[SelectedRowControlIndexes.txtTranslation] as TextBox).Text = translation.Value.Translation;

                    (cell.Controls[txtCommentControlIndex] as TextBox).Text = translation.Value.Comment;
                }
                (row.Cells[editButtonColumNo].FindControl("txtResourceComment") as TextBox).Text = resource.Comment;
            }
        };
    }

    /// <summary>
    /// Shows the textbox controls to edit resources for a resource.
    /// </summary>
    /// <param name="row">The row for which to show the controls.</param>
    /// <param name="visible"><c>true</c> to show the controls, <c>false to hide them</c>.</param>
    private void ToggleEditControls(GridViewRow row, bool visible) {
        int oldRowActiveIndex = visible ? 0 : 1,
            newRowActiveIndex = visible ? 1 : 0;
        if(grdDuplicateDetail.SelectedIndex >= 0 && grdDuplicateDetail.Rows.Count > grdDuplicateDetail.SelectedIndex) {
            GridViewRow oldRow = grdDuplicateDetail.SelectedRow;
            for(int i = editButtonColumNo + 1; i < row.Cells.Count; i++) {
                (oldRow.Cells[i].Controls[0] as MultiView).ActiveViewIndex = oldRowActiveIndex;
            }
        }

        MultiView mltCommands = (row.FindControl("mltActions") as MultiView);
        mltCommands.ActiveViewIndex = newRowActiveIndex;
        row.Cells[2].CssClass = "ssCell"; // remove the comment indicators
        row.Cells[3].CssClass = "ssCell";
        for(int i = editButtonColumNo + 1; i < row.Cells.Count; i++) {
            (row.Cells[i].Controls[0] as MultiView).ActiveViewIndex = newRowActiveIndex;
        }
        if(visible) { // fill the translator comment fields
            string[] languages = (string[])(Session["languages"] ?? new string[0]);
            Dictionary<string, int> columnIndex = Session["colIndexes"] as Dictionary<string, int>;
            int resourceid = int.Parse((mltCommands.FindControl("btnDelete") as ImageButton).CommandArgument);
            int textBoxControlIndex = (row.Cells[editButtonColumNo + 1].Controls[0] as MultiView).Views[1].Controls.Count - 1;// the comments textbox is the last control in the view
            Dictionary<string, string> translatorComments = ResourceBlenderDAL.GetTranslatorComments(resourceid, languages);
            foreach(KeyValuePair<string, string> comment in translatorComments) {
                //System.Diagnostics.Debug.WriteLine(i + "-" + reader.GetName(i) + "-" + comment);
                ((row.Cells[columnIndex[comment.Key]].Controls[0] as MultiView).Views[1].Controls[textBoxControlIndex] as TextBox).Text = comment.Value;
            }
        }
    }

    /// <summary>
    /// Changes the resource selected for an element and populates available resources into their textboxes in the row.
    /// </summary>
    /// <param name="oldRow">The old row.</param>
    /// <param name="selectedResource">The new resource ID.</param>
    private void ChangeDisplayedResource(GridViewRow oldRow, int selectedResource) {
        GridViewRow row = grdDuplicateDetail.Rows[oldRow.RowIndex]; // the index of the old row in the new grid
        ToggleEditControls(row, true);
        DropDownList cboResourceID = (row.Cells[editButtonColumNo].FindControl("cboResourceID") as DropDownList);
        cboResourceID.SelectedValue = selectedResource.ToString();
        row.CssClass = "ssRowSelected";
        if(selectedResource < 0) {
            for(int i = editButtonColumNo + 1; i < row.Cells.Count; i++) {
                ((row.Cells[i].Controls[0] as MultiView).Views[1].Controls[SelectedRowControlIndexes.txtTranslation] as TextBox).Text = "";
            }
        } else {
            int txtCommentControlIndex = (row.Cells[editButtonColumNo + 1].Controls[0] as MultiView).Views[1].Controls.Count - 1;// the comments textbox is the last control in the view
            Dictionary<string, int> columnIndex = Session["colIndexes"] as Dictionary<string, int>;
            string[] languages = (string[])(Session["languages"] ?? new string[0]);
            RBResource res = ResourceBlenderDAL.GetResource(selectedResource, languages);
            foreach(KeyValuePair<string, RBTranslation> translation in res.Translations) {
                View viwEdit = (row.Cells[columnIndex[translation.Key]].Controls[0] as MultiView).Views[1]; // the view containing the edit controls
                (viwEdit.Controls[SelectedRowControlIndexes.txtTranslation] as TextBox).Text = translation.Value.Translation;

                // fill the translators' comment boxes
                (viwEdit.Controls[txtCommentControlIndex] as TextBox).Text = translation.Value.Comment;
            }
            // fill the overall resource comment
            (row.Cells[editButtonColumNo].FindControl("txtResourceComment") as TextBox).Text = res.Comment;
        }
    }

    /// <summary>
    /// Changes elements to use a new resource
    /// </summary>
    /// <param name="oldRow">The row containing the 'selected resource' dropdown box.</param>
    /// <param name="oldId">The old ID.</param>
    private void UpdateResource(GridViewRow oldRow, int oldId) {
        GridViewRow newRow = grdDuplicateDetail.Rows[oldRow.RowIndex];
        Label lblError = grdDuplicateDetail.FooterRow.Cells[editButtonColumNo].FindControl("lblError") as Label;
        int resourceID = int.Parse((oldRow.Cells[editButtonColumNo].FindControl("cboResourceId") as DropDownList).SelectedValue);
        string resourceComment = (oldRow.Cells[3].FindControl("txtResourceComment") as TextBox).Text;
        try {
            Dictionary<string, int> columnIndex = Session["colIndexes"] as Dictionary<string, int>;
            string[] languages = (string[])(Session["languages"] ?? columnIndex.Keys.ToArray());
            //RBResource res = resourceID < 0 ? new RBResource() : ResourceBlenderDAL.GetResource(resourceID, languages);
            RBResource res = new RBResource(resourceID, resourceComment);

            for(int i = editButtonColumNo + 1; i < newRow.Cells.Count; i++) {
                string textBoxID = newRow.Cells[i].Controls[1].UniqueID;
                foreach(string key in Request.Form.AllKeys) {
                    if(key == textBoxID && Request.Form[key].Length > 0) { // because the gridview is dynamic, the controls have to be grabbed from Request.Form
                        string language = textBoxID.Substring(textBoxID.LastIndexOf("$") + 1); // the ID of the textbox has $country-CODE appended to it
                        res.Translations.Add(language, new RBTranslation() {
                            Translation = Request.Form[key],
                            LanguageCode = language,
                            Comment = Request.Form[textBoxID + "-comment"]
                        });
                        break;
                    }
                }
            }
            if(resourceComment.Length > 0) {
                res.Comment = resourceComment;
            }

            try {
                ResourceBlenderDAL.SaveResource(res);
                ResourceBlenderDAL.ReassignElements(oldId.ToString(), resourceID);
                CancelEditing();
                grdDuplicateDetail.DataBind();
            } catch {
                lblError.Text = I18nUtils.GetString("Errors", "ElementNameExists");
                lblError.Visible = true;
                PreserveResource(oldRow.RowIndex, res);
            }
        } catch(Exception ex) {
            lblError.Text = I18nUtils.GetString("Errors", "Error") + ": " + ex.Message;
            lblError.Visible = true;
        }
    }

    /// <summary>
    /// Confirms deletion of a resource or shows an error if it is still used by elements.
    /// </summary>
    /// <param name="row">The row.</param>
    /// <param name="resourceId">The resource id.</param>
    private void ConfirmDelete(GridViewRow row, int resourceId) {
        int numElements = ResourceBlenderDAL.GetAffectedElements(new int[] { resourceId });
        if(numElements > 0) {
            ToggleEditControls(row, true);
            grdDuplicateDetail.SelectedIndex = row.RowIndex;
            Label lblError = row.FindControl("lblError") as Label;
            lblError.Visible = true;
            lblError.Text = I18nUtils.GetString("Errors", "ResourceInUseError");
        } else {
            ToggleEditControls(row, false);
            (row.FindControl("mltActions") as MultiView).ActiveViewIndex = 2;
        }
    }

    /// <summary>
    /// Shows the elements used by a resource.
    /// </summary>
    /// <param name="newRow">The row in the GridView.</param>
    /// <param name="visible">1 if the elements panel should be visible, otherwise false.</param>
    private void ShowElements(GridViewRow newRow, string visible) {
        Panel pnlElementDetail = newRow.FindControl("pnlElementDetail") as Panel;
        if(visible == "1") { // needed because the button ID is used to store the resourceID
            ImageButton imgShowDetails = newRow.FindControl("imgShowDetails") as ImageButton;
            imgShowDetails.ImageUrl = ResolveUrl("~/images/arrow_up.gif");
            imgShowDetails.CommandArgument = "0";
            imgShowDetails.ToolTip = "Hide elements";
            int resourceId = int.Parse((newRow.FindControl("chkSelect") as CheckBox).Text);
            var usage = ResourceBlenderDAL.GetResourceUsage(resourceId); // application, "bundle - element"

            BulletedList lst = null;
            foreach(var application in usage) {
                Literal appHeading = new Literal();
                appHeading.Text = "<strong>" + application.Key + "</strong>";
                pnlElementDetail.Controls.Add(appHeading);
                lst = new BulletedList();
                pnlElementDetail.Controls.Add(lst);
                foreach(var element in application.Value) {
                    lst.Items.Add(element);
                }
            }
            pnlElementDetail.Visible = true;
        }
    }

    /// <summary>
    /// Switches back to the 'Find dupllicates' form, retaining the selected values.
    /// </summary>
    /// <param name="sender">The source of the event.</param>
    /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
    protected void lnkNavDuplicateResources_Click(object sender, EventArgs e) {
        mltMain.ActiveViewIndex = 0;
        BindGrid();
    }

    /// <summary>
    /// Expands/contracts the grid to fill the whole window
    /// </summary>
    /// <param name="sender">The source of the event.</param>
    /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
    protected void lnkExpandScreen_Click(object sender, EventArgs e) {
        if(fullsizeholder.Attributes["class"] == "ssFullSize") {
            lnkExpandScreen.Text = I18nUtils.GetString("UIStrings", "ExpandFillWindow");
            lnkExpandScreen.CssClass = "lnkExpand";
            fullsizeholder.Attributes["class"] = "ssContracted";
        } else {
            lnkExpandScreen.Text = I18nUtils.GetString("UIStrings", "ContractNormalSize");
            lnkExpandScreen.CssClass = "lnkContract";
            fullsizeholder.Attributes["class"] = "ssFullSize";
        }
    }

    #endregion

    #region Merging

    /// <summary>
    /// Checks all boxes in the duplicate details GridView. This is overridden by a JS listener which interrupts the click
    /// </summary>
    /// <param name="sender">The source of the event.</param>
    /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
    protected void btnSelectAll_Click(object sender, EventArgs e) {
        GridViewRowCollection oldRows = grdDuplicateDetail.Rows;
        InitializeDetailsDataSource();
        grdDuplicateDetail.DataBind();
        cboPreferredResource.DataBind();
        foreach(GridViewRow row in oldRows) {
            if(row.RowType == DataControlRowType.DataRow) {
                CheckBox chkSelect = grdDuplicateDetail.Rows[row.RowIndex].FindControl("chkSelect") as CheckBox;
                chkSelect.Checked = true;
            }
        }
    }

    /// <summary>
    /// Adds a JQuery listener for the 'Select All' button
    /// </summary>
    /// <param name="sender">The source of the event.</param>
    /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
    protected void btnSelectAll_PreRender(object sender, EventArgs e) {
        StringBuilder buffer = new StringBuilder(@"$('#").Append(btnSelectAll.ClientID).Append(@"').click(
             function() {").AppendFormat(" $('#{0} INPUT[type=checkbox]').attr('checked', 'true');", grdDuplicateDetail.ClientID).Append(@"
                return false;
             });");
        ClientScript.RegisterStartupScript(typeof(Page), "checkall", buffer.ToString(), true);
    }

    /// <summary>
    /// Merges the selected translations
    /// </summary>
    /// <param name="sender">The source of the event.</param>
    /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
    protected void btnMerge_Click(object sender, EventArgs e) {
        List<int> iDsToMerge = new List<int>();
        StringBuilder errorBuffer = new StringBuilder();
        foreach(GridViewRow row in grdDuplicateDetail.Rows) {
            if(row.RowType == DataControlRowType.DataRow) {
                CheckBox chkSelect = row.FindControl("chkSelect") as CheckBox;
                if(chkSelect.Checked) {
                    try {
                        iDsToMerge.Add(int.Parse(chkSelect.Text));
                    } catch {
                        errorBuffer.AppendLine(I18nUtils.GetString("Errors", "CouldNotReadID") + ": " + chkSelect.Text);
                    }
                }
            }
        }
        bool deleteAfterMerge = chkDeleteUnusedResources.Checked;
        int preferredResource = -1;
        if(!int.TryParse(cboPreferredResource.SelectedValue, out preferredResource)) {
            errorBuffer.AppendLine(I18nUtils.GetString("Errors", "ErrorParsingFmt", I18nUtils.GetString("UIStrings", "PreferredResource")));
        }
        if(iDsToMerge.Count < 2) {
            errorBuffer.AppendLine(I18nUtils.GetString("UIStrings", "NoTranslationsWereSelected"));
        }
        if(errorBuffer.Length > 0) {
            lblMergeError.Text = errorBuffer.ToString();
            lblMergeError.Visible = true;
        } else {
            try {
                ResourceBlenderDAL.MergeResources(iDsToMerge, preferredResource); // todo: option to choose preferred resource
                if(deleteAfterMerge) {
                    ResourceBlenderDAL.DeleteResources(iDsToMerge.Except(new int[] { preferredResource }));
                }
            } catch(Exception ex) {
                plcResults.Controls.Add(new LiteralControl(string.Format("<div class=\"errorDiv\"><h2>{0}</h2>{1}<p>{2}</p></div>", I18nUtils.GetString("Errors", "Error"), ex.Message, I18nUtils.GetString("Errors", "ErrorsCausedReassignSkip"))));
                mltMain.ActiveViewIndex = 2;
            }
        }
        InitializeDetailsDataSource();
        grdDuplicateDetail.DataBind();
        cboPreferredResource.DataBind();
    }

    #endregion
}
